package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.system.model.dto.CourseCategoryDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.content.agent.SystemApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.constant.CoursePubTemplateKey;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CoursePubMsg;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private CourseMarketService courseMarketService;

    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private SystemApiAgent systemApiAgent;

    @Autowired
    private CoursePubMsgService coursePubMsgService;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 课程预览-教学机构
     *
     * @param courseId
     * @param companyId
     * @return
     */
    @Transactional
    public Map<String, Object> preview(Long courseId, Long companyId) {

        //1.构建CoursePub数据并保存
        CoursePub coursePub = generateCoursePub(courseId, companyId, false);


        // 2.根据CoursePub内容构架DataMap数据
        Map<String, Object> dataMap = generateDataMap(coursePub);

        // 3.将DataMap进行返回
        return dataMap;
    }

    private Map<String, Object> generateDataMap(CoursePub coursePub) {

        // 1.构建数据模型对象
        HashMap<String, Object> dataMap = new HashMap<>();

        // 2.构建coursePub
        dataMap.put(CoursePubTemplateKey.COURSEPUB, coursePub);

        // 3.构建courseMarket
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);

        dataMap.put(CoursePubTemplateKey.COURSEMARKET, courseMarket);

        // 4.构建课程计划
        String teachplanJsonString = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJsonString, TeachplanDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE, teachplanDTO);

        // 5.构建课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS, values);

        return dataMap;
    }


    private CoursePub generateCoursePub(Long courseId, Long companyId, boolean isPublish) {

        //1.判断关键数据
        //  courseId companyId

        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家机构
        //         判断是否删除
        //         判断审核状态：教学机构课程预览--未提交、审核未通过
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(courseId, companyId);

        String auditStatus = courseBase.getAuditStatus();

        if (isPublish) {
            if (!(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus))) {
                ExceptionCast.cast(ContentErrorCode.E_120201);
            }
        } else {
            if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                    CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                    CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
            ) {
                ExceptionCast.cast(ContentErrorCode.E_120015);
            }
        }


        //     课程营销
        //         判断是否存在：根据courseid  (courseBaseService.getCourseBaseById 里已经对其进行判断是否存在)
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseId);

        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);


        //  课程计划
        //     获得课程计划：根据courseId和companyId（树形结构）
        TeachplanDTO teachplanTreeNodes = teachplanService.getTreeNodes(courseId, companyId);


        //  课程教师
        //     判断教师信息是否存在：一定要确保课程最少有一个教师信息
        // TODO: 2021/12/17 学员完成


        //  课程分类数据并完善Coursepub数据
        //     调用system服务获得课程分类的名称
        String mt = courseBase.getMt();
        String st = courseBase.getSt();

        RestResponse<CourseCategoryDTO> mtResponse = systemApiAgent.getCourseCategoryById4s(mt);

        if (!(mtResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(mtResponse.getCode(), mtResponse.getMsg());
        }

        RestResponse<CourseCategoryDTO> stResponse = systemApiAgent.getCourseCategoryById4s(st);

        if (!(stResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(stResponse.getCode(), stResponse.getMsg());
        }

        CourseCategoryDTO mtEntity = mtResponse.getResult();
        CourseCategoryDTO stEntity = stResponse.getResult();


        // 构建coursePub数据并执行保存操作
        // 课程基础数据
        CoursePub coursePub = CoursePubConvert.INSTANCE.courseBase2coursePub(courseBase);

        // 课程营销数据
        String marketJsonString = JsonUtil.objectTojson(courseMarket);
        coursePub.setMarket(marketJsonString);
        coursePub.setPrice(courseMarket.getPrice());
        coursePub.setCharge(courseMarket.getCharge());

        // 课程计划数据
        String teachplanJsonString = JsonUtil.objectTojson(teachplanTreeNodes);
        coursePub.setTeachplan(teachplanJsonString);

        // 课程教师
        // TODO:


        // 课程分类数据
        coursePub.setMtName(mtEntity.getName());
        coursePub.setStName(stEntity.getName());


        // 3.保存课程发布数据
        //     CoursePub数据保存数据库中
        //         课程基础信息  课程营销  课程计划  课程教师
        // 一个courseBase数据对应一个coursePub数据
        LambdaQueryWrapper<CoursePub> pubQueryWrapper = new LambdaQueryWrapper<>();
        pubQueryWrapper.eq(CoursePub::getCourseId, courseId);
        pubQueryWrapper.eq(CoursePub::getCompanyId, companyId);

        CoursePub po = this.getOne(pubQueryWrapper);

        boolean result = false;
        // 判断课程发布数据是否存在
        if (ObjectUtils.isEmpty(po)) {
            //     如果不存在
            //             创建课程发布数据
            //     创建课程发布数据时，要将coursePub和courseBase数据进行关联
            coursePub.setCourseId(courseBase.getCourseBaseId());
            result = this.save(coursePub);

        } else {
            //     如果存在
            //             修改课程发布数据内容
            coursePub.setId(po.getId());

            result = this.updateById(coursePub);

        }

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120205);
        }

        // 4.将coursePub数据返回
        return coursePub;
    }



    @Value("${course.publish.exchange}")
    private String exchange;
    @Value("${course.publish.routingkey}")
    private String routingkey;
    /**
     * 课程发布
     *
     * @param courseBaseId
     * @param companyId
     * @param isPublish
     */
    @Transactional
    public void publish(Long courseBaseId, Long companyId, boolean isPublish) {
        // 业务分析：
        //  1.判断关键数据
        if (ObjectUtils.isEmpty(courseBaseId) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        //  2.判断业务数据
        //      课程基本信息
        //          判断是否存在
        //          判断是否是同一家机构
        //          判断是否删除
        //          判断审核状态
        //              只能是审核通过的状态
        //  3.保存数据的业务数据和消息数据
        //      业务数据：coursePubMsg数据进行保存

        CoursePub coursePub = this.generateCoursePub(courseBaseId, companyId, true);

        //      消息数据：coursePubMsg数据进行保存
        //      判断数据库是否有消息数据 有不添加，没有则添加
        LambdaQueryWrapper<CoursePubMsg> msgQueryWrapper = new LambdaQueryWrapper<>();
        msgQueryWrapper.eq(CoursePubMsg::getPubId, coursePub.getId());
        CoursePubMsg coursePubMsg = coursePubMsgService.getOne(msgQueryWrapper);

        if (ObjectUtils.isEmpty(coursePubMsg)) {
            coursePubMsg = new CoursePubMsg();
            coursePubMsg.setCourseId(coursePub.getId());
            coursePubMsg.setCourseId(courseBaseId);
            coursePubMsg.setPubName(coursePub.getName());
            coursePubMsg.setPubStatus(CoursePubMsg.UNSENT);
            coursePubMsg.setCompanyId(companyId);
        }

        boolean result = coursePubMsgService.save(coursePubMsg);

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120206);
        }

        //  4.发送消息给mq
        //      消息必须是有业务唯一标识：courseId
        //      发送消息：发布消息表
        //          转为json字符串
        String msgJsonString = JsonUtil.objectTojson(coursePubMsg);

        // 构造方法中需要指定晓的id值（消息的唯一标识）
        CorrelationData correlationData = new CorrelationData(courseBaseId.toString());

        correlationData.getFuture().addCallback(

                // 交换机响应成功
                confirm -> {
                    boolean ack = confirm.isAck();
                    // 交换机成功接收消息
                    if (ack) {
                        // 交换机成功接收消息后，要修改本地的数据状态
                        execuChangeLocalStatusData(correlationData.getId());
                    }
                },
                // 交换机响应失败
                throwable -> {
                    log.error("发送课程发布的消息失败，失败的标识数据: {}，error: {}", correlationData.getId(), throwable.getMessage());
                }
        );
        rabbitTemplate.convertAndSend(exchange, routingkey, msgJsonString, correlationData);

    }

    /**
     * 修改本地数据的状态值
     *    1.CourseBase
     *      审核状态: 已发布
     *    2.CoursePubMsg
     *      发送成功的状态：0->1
     * @param courseBaseId
     */
    private void execuChangeLocalStatusData(String courseBaseId) {

        // 1.修改课程中基础信息的审核状态
        LambdaUpdateWrapper<CourseBase> courseUpdateWrapper = new LambdaUpdateWrapper<>();
        courseUpdateWrapper.set(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode());
        courseUpdateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        Long courseId = new Long(courseBaseId);
        courseUpdateWrapper.eq(CourseBase::getId, courseId);

        boolean baseResult = courseBaseService.update(courseUpdateWrapper);

        if (!baseResult) {
            log.error("课程发布修改课程审核状态失败: courseBaseId: {}", courseBaseId);
        }

        // 2.修改发布消息表中的状态
        LambdaUpdateWrapper<CoursePubMsg> msgUpdateWrapper = new LambdaUpdateWrapper<>();
        msgUpdateWrapper.set(CoursePubMsg::getPubStatus, CoursePubMsg.SENT);
        msgUpdateWrapper.eq(CoursePubMsg::getCourseId, courseId);

        boolean msgResult = coursePubMsgService.update(msgUpdateWrapper);

        if (!msgResult) {
            log.error("课程发布修改消息状态失败: CourseBaseId: {}", courseBaseId);
        }
    }
}
